﻿using LinnerToolkit.Core.Codec;
using Mina.Core.Future;
using Mina.Core.Session;
using Mina.Filter.Codec;
using Mina.Transport.Serial;
using Mina.Transport.Socket;
using SimulationPTSystem.EnvironmentDevice.Codec;
using SimulationPTSystem.EnvironmentDevice.Enums;
using SimulationPTSystem.EnvironmentDevice.Input;
using SimulationPTSystem.EnvironmentDevice.Models;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows;

namespace SimulationPTSystem.EnvironmentDevice.ViewModels
{
    public class DeviceViewModel
    {
        /// <summary>
        /// 联动设备监听器
        /// </summary>
        public AsyncSocketAcceptor Socket { get; protected set; }
        /// <summary>
        /// 震动舞台连接器
        /// </summary>
        public SerialConnector ShakeConnector { get; protected set; }

        /// <summary>
        /// 红外监听器
        /// </summary>
        public AsyncDatagramAcceptor InfraredAccecptor { get; protected set; }
        /// <summary>
        /// 数据转换器
        /// </summary>
        public IDataConverter DataConverter { get; set; }

        /// <summary>
        /// 设备模型列表
        /// </summary>
        public List<DeviceModel> Models { get; set; }

        /// <summary>
        /// 通过wifi连接震动舞台
        /// </summary>
        protected bool wifiVibrate;

        public DeviceViewModel()
        {
            Models = new List<DeviceModel>();
        }

        public event EventHandler ModelsChanged;
        public event EventHandler Started;
        public event EventHandler Stoped;

        public bool Initialize()
        {
            int localPort;
            if (!int.TryParse(ConfigurationManager.AppSettings["LocalPort"], out localPort))
                return false;

            int infraredPort = -1;
            int.TryParse(ConfigurationManager.AppSettings["InfraredPort"], out infraredPort);

            if(!bool.TryParse(ConfigurationManager.AppSettings["WifiVibrate"],out wifiVibrate))
                wifiVibrate = false;
            int baudRate = 0;
            if (!wifiVibrate && !int.TryParse(ConfigurationManager.AppSettings["BaudRate"], out baudRate))
                return false;

            Socket = new AsyncSocketAcceptor();
            Socket.DefaultLocalEndPoint = new IPEndPoint(IPAddress.Any, localPort);
            Socket.FilterChain.AddLast("codec", new ProtocolCodecFilter(new DefaultDemuxingProtocolCodecFactory(new DataDecoder(), new DataEncoder())));
            Socket.MessageReceived += new EventHandler<IoSessionMessageEventArgs>(Socket_DataReceived);
            Socket.ExceptionCaught += new EventHandler<IoSessionExceptionEventArgs>(Socket_ExceptionCaught);
            Socket.SessionClosed += new EventHandler<IoSessionEventArgs>(Socket_SessionClosed);
            Socket.Deactivated += Socket_Deactivated;
            Socket.Bind();

            // 通过串口连接震动舞台
            if (!wifiVibrate)
            {
                ShakeConnector = new SerialConnector();
                ShakeConnector.FilterChain.AddLast("codec", new ProtocolCodecFilter(new DefaultDemuxingProtocolCodecFactory(new DataDecoder(), new DataEncoder())));
                IConnectFuture future = ShakeConnector.Connect(new SerialEndPoint(ConfigurationManager.AppSettings["PortName"], baudRate, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One));
                future.Await();
            }

            if (infraredPort != -1)
            {
                InfraredAccecptor = new AsyncDatagramAcceptor();
                InfraredAccecptor.DefaultLocalEndPoint = new IPEndPoint(IPAddress.Any, infraredPort);
                InfraredAccecptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new DefaultDemuxingProtocolCodecFactory(new StringDataDecoder())));
                InfraredAccecptor.MessageReceived += InfraredAccecptor_MessageReceived;
                InfraredAccecptor.Bind();
            }

            return true;
        }

        private void InfraredAccecptor_MessageReceived(object sender, IoSessionMessageEventArgs e)
        {
            if (e.Message is StringData stringData)
            {
                switch (stringData.Data)
                {
                    case "stop":
                        Stop();
                        break;
                    case "open0":
                    case "open1":
                    case "open2":
                        try
                        {
                            int index = int.Parse(stringData.Data.Substring(4));
                            Start($"film{index + 2}.txt");
                            //Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                            //{
                            //    MessageBox.Show($"Time:{ DateTime.Now}, Dataa; {stringData.Data}");
                            //}));
                        }
                        catch (Exception ex)
                        {
                            //Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                            //{
                            //    MessageBox.Show($"Message: {ex.Message}\r\nStackTrace: {ex.StackTrace}");
                            //}));
                        }
                        break;
                }
            }
        }

        private void DataInput_DataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data == null || e.Data.Length != 3 || (e.Data[0] == 0xff && e.Data[1] == 0xff && e.Data[2] == 0xff))
                return;

            Send(new EnvironmentDeviceData
            {
                DeviceType = (DeviceType)Enum.ToObject(typeof(DeviceType), e.Data[0]),
                DeviceId = e.Data[1],
                DeviceStatus = (DeviceStatus)Enum.ToObject(typeof(DeviceStatus), e.Data[2]),
            });
        }

        private void Socket_Deactivated(object sender, EventArgs e)
        {
            Socket.Bind();
        }

        private void Socket_SessionClosed(object sender, IoSessionEventArgs e)
        {
            if (Models.FirstOrDefault(m => m.Session == e.Session) is DeviceModel model)
            {
                Models.Remove(model);
                ModelsChanged?.Invoke(this, new EventArgs());
            }
        }

        private void Socket_ExceptionCaught(object sender, IoSessionExceptionEventArgs e)
        {
            e.Session.Close(true);
        }

        private void Socket_DataReceived(object sender, IoSessionMessageEventArgs e)
        {
            if (e.Message is EnvironmentDeviceData data)
            {
                if (Models.FirstOrDefault(m => m.Session == e.Session) is DeviceModel model)
                {
                    model.Id = data.DeviceId;
                    model.Type = data.DeviceType;
                    model.Status = data.DeviceStatus;
                }
                else if (Models.FirstOrDefault(m => m.Id == data.DeviceId && m.Type == data.DeviceType) is DeviceModel deviceModel)
                {
                    deviceModel.Session = e.Session;
                    deviceModel.Status = data.DeviceStatus;
                }
                else
                {
                    Models.Add(new DeviceModel
                    {
                        Session = e.Session,
                        Id = data.DeviceId,
                        Type = data.DeviceType,
                        Status = data.DeviceStatus,
                    });

                    ModelsChanged?.Invoke(this, new EventArgs());
                }
            }
        }

        /// <summary>
        /// 开始联动
        /// </summary>
        /// <param name="dataFileName">配置文件名</param>
        public void Start(string dataFileName = "")
        {
            Stop();

            DataConverter = new ConfigDataConverter(dataFileName);
            if (!DataConverter.Initialize())
                return;
            DataConverter.DataReceived += DataInput_DataReceived;
            DataConverter?.StartAsync();

            Started?.Invoke(this, new EventArgs());
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data"></param>
        public void Send(EnvironmentDeviceData data)
        {
            if (data == null)
                return;

            IoSession session = null;
            if(!wifiVibrate&&data.DeviceType== DeviceType.VibrateMachine)
            {
                if (ShakeConnector.ManagedSessions.FirstOrDefault() is KeyValuePair<long, IoSession> shakeSession)
                    session = shakeSession.Value;
            }
            else
            {
                if (Models.FirstOrDefault(m => m.Id == data.DeviceId && m.Type == data.DeviceType) is DeviceModel deviceModel)
                    session = deviceModel.Session;
            }

            if (session != null && session.Connected)
                session.Write(data);
        }

        public void Stop()
        {
            if (DataConverter != null)
            {
                DataConverter?.StopAsync();
                DataConverter.DataReceived -= DataInput_DataReceived;
                DataConverter = null;
            }

            for (int i = 0; i < Models.Count; i++)
            {
                Send(new EnvironmentDeviceData
                {
                    DeviceId = Models[i].Id,
                    DeviceType = Models[i].Type,
                    DeviceStatus = DeviceStatus.Off
                });
                Task.Delay(100).Wait();
            }

            Stoped?.Invoke(this, new EventArgs());
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (Socket != null)
            {
                foreach(var session in Socket.ManagedSessions.Values)
                {
                    if (session.Connected)
                        session.Close(true);
                }
                Socket.Unbind();
                Socket.Dispose();
                Socket = null;
            }

            if (ShakeConnector != null)
            {
                foreach (var session in ShakeConnector.ManagedSessions.Values)
                {
                    if (session.Connected)
                        session.Close(true);
                }
                ShakeConnector.Dispose();
                ShakeConnector = null;
            }

            if (InfraredAccecptor != null)
            {
                foreach (var session in InfraredAccecptor.ManagedSessions.Values)
                {
                    if (session.Connected)
                        session.Close(true);
                }
                InfraredAccecptor.Unbind();
                InfraredAccecptor.Dispose();
                InfraredAccecptor = null;
            }

            Models.Clear();
        }
    }
}
